﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace UtilsLib.UtilsExtensions.String
{
    /// <summary>
    /// Classe de méthodes d'extension pour le type 'string'
    /// </summary>
    public static class StringExtensions
    {
        /// <summary>
        /// Convert the special chars "é", "$", ... in their HTML equivalent --> "&eacute;", ...
        /// </summary>
        /// <param name="text">The text to convert</param>
        /// <returns>The text with it's special char converted to html</returns>
        public static string ConvertStringToHTMLString(this string text, bool quote)
        {
            string result = text;

            //special char
            result = result.Replace("&", "&amp;");
            if (quote)
                result = result.Replace("\"", "&ldquo;");
            result = result.Replace("'", "&lsquo;");

            //a
            result = result.Replace("á", "&aacute;");
            result = result.Replace("à", "&agrave;");
            result = result.Replace("ä", "&auml;");
            result = result.Replace("â", "&acirc;");
            result = result.Replace("ã", "&tilde;");
            result = result.Replace("Á", "&Aacute;");
            result = result.Replace("À", "&Agrave;");
            result = result.Replace("Ä", "&Auml;");
            result = result.Replace("Â", "&Acirc;");
            result = result.Replace("Ã", "&Atilde;");
            //e
            result = result.Replace("é", "&eacute;");
            result = result.Replace("è", "&egrave;");
            result = result.Replace("ë", "&euml;");
            result = result.Replace("ê", "&ecirc;");
            result = result.Replace("É", "&Eacute;");
            result = result.Replace("È", "&Egrave;");
            result = result.Replace("Ë", "&Euml;");
            result = result.Replace("Ê", "&Ecirc;");
            //i
            result = result.Replace("í", "&iacute;");
            result = result.Replace("ì", "&igrave;");
            result = result.Replace("ï", "&iuml;");
            result = result.Replace("î", "&icirc;");
            result = result.Replace("Í", "&Iacute;");
            result = result.Replace("Ì", "&Igrave;");
            result = result.Replace("Ï", "&Iuml;");
            result = result.Replace("Î", "&Icirc;");
            //o
            result = result.Replace("ó", "&oacute;");
            result = result.Replace("ò", "&ograve;");
            result = result.Replace("ö", "&ouml;");
            result = result.Replace("ô", "&ocirc;");
            result = result.Replace("õ", "&otilde;");
            result = result.Replace("Ó", "&Oacute;");
            result = result.Replace("Ò", "&Ograve;");
            result = result.Replace("Ö", "&Ouml;");
            result = result.Replace("Ô", "&Ocirc;");
            result = result.Replace("Õ", "&Otilde;");
            //u
            result = result.Replace("ú", "&uacute;");
            result = result.Replace("ù", "&ugrave;");
            result = result.Replace("ü", "&uuml;");
            result = result.Replace("û", "&ucirc;");
            result = result.Replace("Ú", "&Uacute;");
            result = result.Replace("Ù", "&Ugrave;");
            result = result.Replace("Ü", "&Uuml;");
            result = result.Replace("Û", "&Ucirc;");
            //n
            result = result.Replace("ñ", "&ntilde;");
            result = result.Replace("Ñ", "&Ntilde;");

            return result;
        }

        /// <summary>
        /// Convert the HTML chars "&eacute;", ... in their string equivalent --> "é", "$", ...
        /// </summary>
        /// <param name="text">The text to convert</param>
        /// <returns>The text with it's special char converted to html</returns>
        public static string ConvertHTMLStringToString(this string text)
        {
            string result = text;

            //special char
            result = result.Replace("&amp;", "&");
            result = result.Replace("&ldquo;", "\"");
            result = result.Replace("&lsquo;", "'");

            //a
            result = result.Replace("&aacute;", "á");
            result = result.Replace("&agrave;", "à");
            result = result.Replace("&auml;", "ä");
            result = result.Replace("&acirc;", "â");
            result = result.Replace("&tilde;", "ã");
            result = result.Replace("&Aacute;", "Á");
            result = result.Replace("&Agrave;", "À");
            result = result.Replace("&Auml;", "Ä");
            result = result.Replace("&Acirc;", "Â");
            result = result.Replace("&Atilde;", "Ã");
            //e
            result = result.Replace("&eacute;", "é");
            result = result.Replace("&egrave;", "è");
            result = result.Replace("&euml;", "ë");
            result = result.Replace("&ecirc;", "ê");
            result = result.Replace("&Eacute;", "É");
            result = result.Replace("&Egrave;", "È");
            result = result.Replace("&Euml;", "Ë");
            result = result.Replace("&Ecirc;", "Ê");
            //i
            result = result.Replace("&iacute;", "í");
            result = result.Replace("&igrave;", "ì");
            result = result.Replace("&iuml;", "ï");
            result = result.Replace("&icirc;", "î");
            result = result.Replace("&Iacute;", "Í");
            result = result.Replace("&Igrave;", "Ì");
            result = result.Replace("&Iuml;", "Ï");
            result = result.Replace("&Icirc;", "Î");
            //o
            result = result.Replace("&oacute;", "ó");
            result = result.Replace("&ograve;", "ò");
            result = result.Replace("&ouml;", "ö");
            result = result.Replace("&ocirc;", "ô");
            result = result.Replace("&otilde;", "õ");
            result = result.Replace("&Oacute;", "Ó");
            result = result.Replace("&Ograve;", "Ò");
            result = result.Replace("&Ouml;", "Ö");
            result = result.Replace("&Ocirc;", "Ô");
            result = result.Replace("&Otilde;", "Õ");
            //u
            result = result.Replace("&uacute;", "ú");
            result = result.Replace("&ugrave;", "ù");
            result = result.Replace("&uuml;", "ü");
            result = result.Replace("&ucirc;", "û");
            result = result.Replace("&Uacute;", "Ú");
            result = result.Replace("&Ugrave;", "Ù");
            result = result.Replace("&Uuml;", "Ü");
            result = result.Replace("&Ucirc;", "Û");
            //n
            result = result.Replace("&ntilde;", "ñ");
            result = result.Replace("&Ntilde;", "Ñ");

            return result;
        }

        /// <summary>
        /// int.TryParse
        /// </summary>
        /// <param name="text"></param>
        /// <param name="defaultValue">Value if conversion fails</param>
        /// <returns></returns>
        public static int TryParseToInt(this string text, int defaultValue)
        {
            int res = defaultValue;
            int.TryParse(text, out res);
            return res;
        }

        /// <summary>
        /// TryParse, null if fails
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int? ToNullableInt(this string s)
        {
            int res = 0;
            if (int.TryParse(s, out res))
                return res;
            else
                return null;
        }

        /// <summary>
        /// Get the first word in a string
        /// </summary>
        /// <param name="text">The text to process</param>
        /// <param name="delimiter">The word delimiter</param>
        /// <returns>The first word in the text</returns>
        public static string GetFirstWord(this string text, string delimiter)
        {
            if (text.IndexOf(delimiter) == -1)
                return text;
            return text.Substring(0, text.IndexOf(delimiter)).Trim();
        }

        /// <summary>
        /// Get the string between the startContent and the EndContent in the text.
        /// </summary>
        /// <param name="text">The string containing everything !</param>
        /// <param name="startContent">The string used as openingtag</param>
        /// <param name="endContent">the string used as closing tag</param>
        /// <param name="includeStart">Specify if you want to include opening tag</param>
        /// <returns>The text between the string limits</returns>
        public static string GetStringBetweenContent(this string text, string startContent, string endContent, bool includeStart)
        {
            int from = text.IndexOf(startContent) + (includeStart ? 0 : startContent.Length);
            int to = endContent != "" ? text.IndexOf(endContent) : text.Length;
            return text.Substring(from, to - from);
        }

        /// <summary>
        /// Get the content nested between the tags : <[tag] [param]>Content</[tag]>
        /// </summary>
        /// <param name="text">the string containing the tags</param>
        /// <param name="tag">the tag enveloping the content</param>
        /// <param name="param">the parameter string of the tag</param>
        /// <returns>the inner string between the tags</returns>
        public static string GetStringBetweenTag(this string text, string tag, string param)
        {
            int from = text.IndexOf("<" + tag + " " + param + ">") + tag.Length + 2;
            int to = text.IndexOf("</" + tag + ">");
            return text.Substring(from, to - from);
        }

        /// <summary>
        /// Get the content nested between the tags : <[tag]>Content</[tag]>
        /// </summary>
        /// <param name="text">the string containing the tags</param>
        /// <param name="tag">the tag enveloping the content</param>
        /// <returns>the inner string between the tags</returns>
        public static string GetStringBetweenTag(this string text, string tag)
        {
            int from = text.IndexOf("<" + tag + ">") + tag.Length + 2;
            int to = text.IndexOf("</" + tag + ">");
            return text.Substring(from, to - from);
        }

        /// <summary>
        /// Get the number of [trimvalue] to trim at the left of the [text]
        /// </summary>
        /// <param name="text">the string to trim</param>
        /// <param name="trimvalue">the value to trim</param>
        /// <returns></returns>
        public static int GetNbToTrimStringLeft(this string text, string trimvalue)
        {

            string textTemp = text;
            int result = 0;
            while (textTemp.Length >= trimvalue.Length && textTemp.Substring(0, trimvalue.Length) == trimvalue)
            {
                textTemp = textTemp.Substring(trimvalue.Length);
                result++;
            }

            return result;
        }

        /// <summary>
        /// Get the number of [trimvalue] to trim at the right of the [text]
        /// </summary>
        /// <param name="text">the text to trim</param>
        /// <param name="trimvalue">the value to trim</param>
        /// <returns></returns>
        public static int GetNbToTrimStringRight(this string text, string trimvalue)
        {

            string textTemp = text;
            int result = 0;
            while (textTemp.Length > trimvalue.Length && textTemp.Substring(textTemp.Length - 1 - trimvalue.Length) == trimvalue)
            {
                textTemp = textTemp.Substring(0, textTemp.Length - trimvalue.Length);
                result++;
            }

            return result;
        }

        /// <summary>
        /// Get the concatenation of [times] * [str]
        /// </summary>
        /// <param name="str">The string to repeat</param>
        /// <param name="times">The number of times to repeat the string
        /// </param>
        /// <returns></returns>
        public static string GetStringXTime(this string str, int times)
        {
            int i = 0;
            string res = "";
            while (i < times)
            {
                res += str;
                i++;
            }
            return res;
        }
        
        /// <summary>
        /// Get the substring of words delimited by a specified delimiter between the desired positions.
        /// </summary>
        /// <param name="text">The text to process</param>
        /// <param name="startPos">The starting position</param>
        /// <param name="endPos">The ending position</param>
        /// <param name="delimiter">The word delimiter</param>
        /// <returns>The words between the starting and ending position</returns>
        public static string GetWordsBetweenPositions(this string text, int startPos, int endPos, string delimiter)
        {
            int length = endPos - startPos;
            string temp = text.Substring(startPos);
            // attention au cas ou la longueur indiquer est plus grande que la longueur de la sous chaine
            while (temp.Length > length)
            {
                temp = temp.Remove(temp.LastIndexOf(delimiter));
            }
            return temp;
        }

        /// <summary>
        /// IsNullOrEmpty
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool INOE(this string text)
        {
            return string.IsNullOrEmpty(text);
        }

        /// <summary>
        /// IsNullOrWhiteSpace
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool INOW(this string text)
        {
            return string.IsNullOrWhiteSpace(text);
        }

        /// <summary>
        /// Remove the XML tag and it's nested content
        /// </summary>
        /// <param name="text">the text to change</param>
        /// <param name="tag"> the tag to remove</param>
        /// <returns>the modified text</returns>
        public static string RemoveStringBetweenXMLTag(this string text, string tag)
        {
            int from = text.IndexOf("<" + tag + ">") + tag.Length + 2;
            int to = text.Substring(from).IndexOf("</" + tag + ">") + from;
            return text.Substring(0, from - tag.Length - 2) + text.Substring(to + tag.Length + 3);
        }

        /// <summary>
        /// Remove the first XML tag with a specified option from the text
        /// </summary>
        /// <param name="text"> the text to modify</param>
        /// <param name="tag">the tag to remove</param>
        /// <param name="option"> the option</param>
        /// <param name="optionvalue">the value of the option</param>
        /// <returns></returns>
        public static string RemoveStringBetweenXMLTag(this string text, string tag, string option, string optionvalue)
        {
            string tagOpen = "<" + tag + " " + option + "=\"" + optionvalue + "\">";

            int from = text.IndexOf(tagOpen) + tagOpen.Length;
            int to = text.Substring(from).IndexOf("</" + tag + ">") + from;
            return text.Substring(0, from - tagOpen.Length) + text.Substring(to + tag.Length + 3);
        }

        /// <summary>
        /// Remove all the XML tag with a specified option from the text
        /// </summary>
        /// <param name="text"> the text to modify</param>
        /// <param name="tag">the tag to remove</param>
        /// <param name="option"> the option</param>
        /// <param name="optionvalue">the value of the option</param>
        public static string RemoveStringBetweenAllXMLTag(this string text, string tag, string option, string optionvalue)
        {
            string tagOpen = "<" + tag + " " + option + "=\"" + optionvalue + "\">";
            string result = text;
            while (result.IndexOf(tagOpen) != -1)
                result = RemoveStringBetweenXMLTag(result, tag, option, optionvalue);
            return result;
        }

        /// <summary>
        /// Remove the bracketed content in the text
        /// </summary>
        /// <param name="text">text to process</param>
        /// <param name="bracketStart">string value of opening bracket</param>
        /// <param name="bracketEnd">string value of closing bracket</param>
        /// <returns>The string without the bracket and it's content</returns>
        public static string RemoveBracket(this string text, string bracketStart, string bracketEnd)
        {
            int indexStart = text.IndexOf(bracketStart);
            int indexEnd = text.IndexOf(bracketEnd);
            if (indexStart >= 0 && indexEnd > 0)
                return text.Substring(0, indexStart) + " " + text.Substring(indexEnd + 1, (text.Length - indexEnd - 1));
            else
                return text;
        }

        /// <summary>
        /// Remove a specific word in a texte
        /// </summary>
        /// <param name="text">The text in wich you want to remove the word</param>
        /// <param name="word">The word you want to remove</param>
        /// <returns>The texte without the first occurence of the word</returns>
        public static string RemoveWordInText(this string text, string word)
        {
            return RemoveContent(text, text.IndexOf(word), word.Length);
        }

        /// <summary>
        /// Delete the content from startposition during length char
        /// </summary>
        /// <param name="text">The texte to apply the change to</param>
        /// <param name="startPosition">The starting position</param>
        /// <param name="length">The length of the delete</param>
        /// <returns>The texte with a slimfast cure</returns>
        public static string RemoveContent(this string text, int startPosition, int length)
        {
            string result = "";
            if (startPosition != 0)
                result += text.Substring(0, startPosition - 1);
            result += text.Substring(startPosition + length, text.Length - (startPosition + length));
            return result;
        }
        
        /// <summary>
        /// Remove diacritics from char
        /// </summary>
        /// <param name="input">The string to remove diacritics from</param>
        /// <returns>the string without diacritics</returns>
        public static string RemoveDiacritics(this string input, string spaceChar="_")
        {
            if (input != null)
            {
                var normalizedString = input.Normalize(NormalizationForm.FormD);
                var stringBuilder = new StringBuilder();

                foreach (var c in normalizedString)
                {
                    var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
                    if (unicodeCategory != UnicodeCategory.NonSpacingMark)
                    {
                        stringBuilder.Append(c);

                    }
                }

                return (stringBuilder.ToString().Normalize(NormalizationForm.FormC).Replace(" ", spaceChar));
            }
            return "";
        }

        
        public static string PrepStringForRegex(this string text)
        {
            string result = text.Replace("+", "\\+");
            result = result.Replace("*", "\\*");
            result = result.Replace("$", "\\$");
            result = result.Replace("^", "\\^");
            result = result.Replace(".", "\\.");
            result = result.Replace("?", "\\?");

            return result;
        }

        public static string PrepStringForUrl(this string text, string spaceChar="_")
        {
            string result = RemoveDiacritics(text, spaceChar);
            result =
                 result.Replace(" ", spaceChar)
                .Replace(",", spaceChar)
                .Replace("+", spaceChar)
                .Replace("=", spaceChar)
                .Replace(":", spaceChar)
                .Replace(".",spaceChar)
                .Replace("?",spaceChar)
                .Replace("'", spaceChar)
                .Replace("`", spaceChar)
                .Replace("~", spaceChar)
                .Replace("$", spaceChar)
                .Replace("*", spaceChar)
                .Replace("!",spaceChar)
                .Replace("&",spaceChar)
                .Replace("%",spaceChar)
                .Replace("#",spaceChar)
                .Replace(";",spaceChar)
                .Replace("\\", spaceChar)
                .Replace("\"", spaceChar);

            while (result.IndexOf(spaceChar + spaceChar) >= 0)
                result = result.Replace(spaceChar + spaceChar, spaceChar);
            return result;
        }

        /// <summary>
        /// Trim the text of the specified string in the front and the back of the text.
        /// </summary>
        /// <param name="text">Text to trim</param>
        /// <param name="trimvalue">value to remove from the front and the back of the text.</param>
        /// <returns></returns>
        public static string TrimString(this string text, string trimvalue)
        {

            string result = text;
            while (result.Length >= trimvalue.Length && result.Substring(0, trimvalue.Length) == trimvalue)
            {
                result = result.Substring(trimvalue.Length);
            }

            while (result.Length > trimvalue.Length && result.Substring(result.Length - 1 - trimvalue.Length) == trimvalue)
            {
                result = result.Substring(0, result.Length - trimvalue.Length);
            }

            return result;
        }

        /// <summary>
        /// Formats a string with the provided parameters
        /// </summary>
        /// <param name="s">
        /// The s.
        /// </param>
        /// <param name="args">
        /// The args.
        /// </param>
        /// <returns>
        /// The formatted string
        /// </returns>
        public static string FormatWithArgs(this string s, params object[] args)
        {
            return s.INOW() ? null : System.String.Format(s, args);
        }
    }
}
